package com.cy.pj.common.aspect;

import java.lang.reflect.Method;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.common.util.IPUtils;
import com.cy.pj.common.util.ShiroUtils;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;

/**
 * 本切面处理存储用户日志的操作
 * @author private
 *
 */
@Order(1)
@Aspect
@Slf4j
@Component
public class SysLogAspect {
	
	//使用注解方式指定环绕通知加到什么方法上
	@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
	public void logPointcut() {}
	
	@Around("logPointcut()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		long start = System.currentTimeMillis();
		log.info("start:{}",start);
		try {
			Object result = joinPoint.proceed();
			long end = System.currentTimeMillis();
			log.info("end:{}",end);
			Long time = end-start;

			//将用户行为写到数据库中--如果写日志会非常耗时,那么我们的解决方法是?
			saveSysLog(joinPoint,time);
			
			return result;
		}catch (Exception e) {
			log.error("error:{}",e.getMessage());
			throw e;
		}
	}
	
	/**
	 * 此方法进行用户操作日志的记录
	 * 需要记录的为: username用户名,operation操作,method方法的完全限定名,params传入的参数
	 * time方法执行时长,ip客户端ip,createdTime创建时间
	 * @param joinPoint
	 * @param time
	 * @throws JsonProcessingException 
	 */
	@Autowired
	private SysLogService sysLogService;
	private void saveSysLog(ProceedingJoinPoint joinPoint, Long time) throws JsonProcessingException, NoSuchMethodException, SecurityException {
		//获取正在执行的署名对象
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		System.out.println(signature);
		//获取其用户名,先指定为固定的admin
		String username = ShiroUtils.getUsername();
		//获取其全限定名和方法名
		String methodName = signature.getName();
		String className = signature.getDeclaringTypeName();
		String sysMethod = className+"."+methodName;
		//获取其方法上的注解(方法上的RequiredLog注解)
		//这里只能使用joinPoint,使用signature获取到的为接口,如果使用Cglib代理则代理为子类,可以使用signature,JDK代理底层代理会实现接口,内部耦合接口对象进行调用,所以使用signature
		//public abstract int com.cy.pj.sys.service.SysUserService.validById(java.lang.Integer,java.lang.Integer)
		Class<? extends Object> cls = joinPoint.getTarget().getClass();//一定会得到正在被代理类,而不是接口
		Class<?>[] parameterTypes = signature.getParameterTypes();
		Method method = cls.getDeclaredMethod(methodName, parameterTypes);
		System.out.println(cls);
		System.out.println(method);
		RequiredLog annotation = method.getAnnotation(RequiredLog.class);
		//获取传入的参数
		Object[] args = joinPoint.getArgs();
		//将参数类型转换为Json字符串格式
		
		String params = new ObjectMapper().writeValueAsString(args);
		Date createdTime = new Date();
		//将值注入到SysLog对象中
		SysLog sysLog = new SysLog();
		sysLog.setUsername(username);
		//if(annotation!=null) {//使用细粒度表达式,则进入此切面的一定为RequiredLog注解包含的,所以不用进行判断
			sysLog.setOperation(annotation.key());
		//}
		sysLog.setMethod(sysMethod);
		sysLog.setParams(params);
		sysLog.setTime(time);
		sysLog.setIp(IPUtils.getIpAddr());
		sysLog.setCreatedTime(createdTime);
		
		sysLogService.insertObject(sysLog);
				
			
		
	}
	
}
